#include "SIMULATION_core.h"

namespace PRS           // PRS: Petroleum Reservoir Simulator
{
	SIMULATION_core::SIMULATION_core(){
	}

	SIMULATION_core::~SIMULATION_core(){
	}

	int SIMULATION_core::tsSolver(){
		/*
		 * if restart is required continue simulation from where it stopped otherwise returns 0.
		 */
		double timeStep;

		/*
		 * Open log file monitor
		 */
		LogFiles(OPENLG,0,0,0,0,pSimPar->getOutputPathName(),
				pSimPar->useRestart(),
				pSimPar->getTStepNumber(),			/* if restart=false, returns 0*/
				pSimPar->getCPU_time());			/* if restart=false, returns .0*/

		int counter = 0;
		double time_step_summation = .0;

		while ( !pSimPar->finishSimulation() ){
			// solve elliptic/diffusion equation

			double t1 = pElliptic_eq->solver(pTMesh_data);

			// solve hyperbolic/advective equation
			double t2 = pHyperbolic_eq->solver(pTMesh_data,timeStep);

			time_step_summation += timeStep;
			PetscPrintf(PETSC_COMM_WORLD,"#Step:\t%d\nTime-step:\t%.8E\nAccumulated:\t%.8E\n",++counter,timeStep,time_step_summation);

			// Output data (Log)
			LogFiles(UPDATELG,t1,t2,timeStep,pSimPar->getAccumulatedSimulationTime());

			//STOP();

		}
		LogFiles(CLOSELG);
		return 0;
	}

	int SIMULATION_core::initialize(int argc, char **argv){
		/*
		 * Initialize parallel libraries
		 */
		PetscInitialize(&argc,&argv,(char *)0,(char *)0);

		printSimulationHeader();

		/*
		 * Initialize simulation pointers
		 */
		pPPData = new PhysicPropData;
		pSimPar = new SimulatorParameters;
		pMData = new MeshData(pSimPar);
		pTMesh_data = new TMesh;

		/*
		 * Load data from files
		 */
		pSimPar->inputDataFromFiles(pTMesh_data);

		/*
		 *  Initialization procedure based on previous loaded data from file:
		 *  	- well flow
		 *  	- physical properties
		 *  	- boundary and initial conditions, mapping
		 */
		pSimPar->initialize(pTMesh_data);
		pPPData->initialize(pMData,pSimPar,pTMesh_data);
		pMData->initialize(pTMesh_data);
		PetscPrintf(PETSC_COMM_WORLD,"Number of processes required: %d\n",P_size());

		/*
		 * Oil production output
		 */
		if ( pSimPar->rankHasProductionWell() ) {
			string path = pSimPar->getOutputPathName();
			char tmp[256]; sprintf(tmp,"%s_oil-production-%d.csv",path.c_str(),1);
			string FileName(tmp);
			pOilProduction = new OilProductionManagement(FileName,pSimPar->getInitialOilVolume(),pSimPar->getTotalInjectionFlowRate());
		}

		/*
		 *  Initialize elliptic and hyperbolic solver pointers
		 */
		pElliptic_eq = init_EllipticSolverPointer( pSimPar->getEllipticSolver() );
		pHyperbolic_eq = init_HyperbolicSolverPointer( pSimPar->getHyperbolicSolver() );
		return 0;
	}


		Elliptic_equation* SIMULATION_core::init_EllipticSolverPointer(int elliptic_method){
			switch ( elliptic_method ){
			case 1:
				return new EBFV1_elliptic(pPPData,pSimPar,pMData);
				break;
			default:
				throw Exception(__LINE__,__FILE__,"Could not initialize a pointer to pElliptic_eq. Unknown method.\n");
			}
		}

		Hyperbolic_equation* SIMULATION_core::init_HyperbolicSolverPointer(int hyperbolic_method){
			switch ( hyperbolic_method ){
			case 1:
				return new EBFV1_hyperbolic(pPPData,pSimPar,pMData,pOilProduction,pTMesh_data);
				break;
			case 2:
				return  new EBFV1_hyperbolic_adaptative(pPPData,pSimPar,pMData,pOilProduction,pTMesh_data);
			default:
				throw Exception(__LINE__,__FILE__,"Could not initialize a pointer to pHiperbolic_eq. Unknown method.\n");
			}
		}

	int SIMULATION_core::finalize(){
		// Write to file oil production output. Only rank 0 is in charge of it.
		string path = pSimPar->getOutputPathName();
		char tmp[256]; sprintf(tmp,"%s_PETSc_summary_nproc%d.log",path.c_str(),1);
		PetscErrorCode ierr = PetscLogPrintSummary(MPI_COMM_WORLD,tmp); CHKERRQ(ierr);
		delete pPPData;
		delete pSimPar;
		delete pMData;
		return 0;
	}
}
